<?php

class MsggWechatClient extends MsggChannelBase
{
    const API_DEPARTMENT_LIST = 'department_list';
    const API_USER_CREATE = 'user_create';
    const API_USER_UPDATE = 'user_update';
    const API_USER_LIST = 'user_list';
    const API_USER_GET = 'user_get';
    const API_MESSAGE_SEND = 'message_send';

    const CONF_DEFAULT_CORP_ID = 'wxc9f52c6b045aebdb';
    const CONF_DEFAULT_CORP_SECRET = 'vtSEe2Ak1ou3kkv2CARnHeJ0I2Q1wBTwO0V4Wu5EkWtuT5pLvoy3u_dLpiUNRJOC';
    const CONF_DEFAULT_MSG_APP_ID = 4;
    const CONF_DEFAULT_DEPARTMENT_ID = 4;

    const MSG_TYPE_TEXT = 'text';
    const MSG_TYPE_IMAGE = 'image';
    const MSG_TYPE_VOICE = 'voice';
    const MSG_TYPE_VIDEO = 'video';
    const MSG_TYPE_FILE = 'file';
    const MSG_TYPE_NEWS = 'news';

    const CURL_METHOD_GET = 'get';
    const CURL_METHOD_POST = 'post';

    const ERROR_CODE_FAIL = 1;

    const MAX_CONTENT_SIZE = '2000';

    private static $_conf = array();

    public static function sendMsg($params, $conf)
    {
        self::$_conf = $conf;
        $result = array('code' => 0, 'msg' => '', 'data' => array());
        try {
            if (isset($params['msgtype'])  && $params['msgtype'] == self::MSG_TYPE_TEXT&& isset($params[self::MSG_TYPE_TEXT]['content']) && strlen($params[self::MSG_TYPE_TEXT]['content']) >= self::MAX_CONTENT_SIZE) {
                Yii::log('wechat content too long ' . strlen($params[self::MSG_TYPE_TEXT]['content']), CLogger::LEVEL_INFO, __METHOD__);
                $content = $params[self::MSG_TYPE_TEXT]['content'];
                $content = (substr($content, 0, self::MAX_CONTENT_SIZE - 100) . '.....内容超过2048字节此处省略' . (strlen($content) - self::MAX_CONTENT_SIZE + 100).'字节');
                $params[self::MSG_TYPE_TEXT]['content'] = $content;
            }
            $params['agentid'] = self::getConf('msg_app_id', self::CONF_DEFAULT_MSG_APP_ID);
            $resp = self::curlApi(self::API_MESSAGE_SEND, self::CURL_METHOD_POST, $params);
            if ($resp['errcode'] != 0) {
                throw new Exception('response errcode abnormal:' . $resp['errcode'], self::ERROR_CODE_FAIL);
            } elseif ($resp['errcode'] == 0 && trim($resp['invaliduser']) != '') {
                throw new Exception('invaliduser: ' . $resp['invaliduser'], self::ERROR_CODE_FAIL);
            }
            $result['data'] = $resp;
        } catch (Exception $e) {
            $result['code'] = self::ERROR_CODE_FAIL;
            $result['msg'] = $e->getMessage();
        }
        $result['ext']['class'] = self::class;
        return $result;
    }

    public static function getDepartments($params = array(), $conf = array())
    {
        self::$_conf = $conf;
        $result = array('code' => 0, 'msg' => '', 'data' => array());
        try {
            $resp = self::curlApi(self::API_DEPARTMENT_LIST, self::CURL_METHOD_GET, $params);
            $result['code'] = $resp['errcode'];
            $result['msg'] = empty($resp['errmsg']) ? '' : $resp['errmsg'];
            $result['data'] = (empty($resp['department']) || !is_array($resp['department'])) ? array() : $resp['department'];
        } catch (Exception $e) {
            $result['code'] = self::ERROR_CODE_FAIL;
            $result['msg'] = $e->getMessage();
        }
        return $result;
    }

    public static function createUser($params, $conf = array())
    {
        self::$_conf = $conf;
        $result = array('code' => 0, 'msg' => '', 'data' => array());
        try {
            if (empty($params['department'])) {
                $params['department'] = array(self::CONF_DEFAULT_DEPARTMENT_ID);
            }
            $resp = self::curlApi(self::API_USER_CREATE, self::CURL_METHOD_POST, $params);
            $result['code'] = $resp['errcode'];
            $result['msg'] = empty($resp['errmsg']) ? '' : $resp['errmsg'];
        } catch (Exception $e) {
            $result['code'] = self::ERROR_CODE_FAIL;
            $result['msg'] = $e->getMessage();
        }
        return $result;
    }

    public static function updateUser($params = array(), $conf = array())
    {
        self::$_conf = $conf;
        $result = array('code' => 0, 'msg' => '', 'data' => array());
        try {
            if (empty($params['department'])) {
                $params['department'] = array(self::CONF_DEFAULT_DEPARTMENT_ID);
            }
            $resp = self::curlApi(self::API_USER_UPDATE, self::CURL_METHOD_POST, $params);
            $result['code'] = $resp['errcode'];
            $result['msg'] = empty($resp['errmsg']) ? '' : $resp['errmsg'];
        } catch (Exception $e) {
            $result['code'] = self::ERROR_CODE_FAIL;
            $result['msg'] = $e->getMessage();
        }
        return $result;
    }

    public static function getUser($params, $conf = array())
    {
        self::$_conf = $conf;
        $result = array('code' => 0, 'msg' => '', 'data' => array());
        try {
            $resp = self::curlApi(self::API_USER_GET, self::CURL_METHOD_GET, $params);
            $result['code'] = $resp['errcode'];
            $result['msg'] = empty($resp['errmsg']) ? '' : $resp['errmsg'];
            $result['data'] = $resp;
        } catch (Exception $e) {
            $result['code'] = self::ERROR_CODE_FAIL;
            $result['msg'] = $e->getMessage();
        }
        return $result;
    }

    public static function getUserList($params, $conf = array())
    {
        self::$_conf = $conf;
        $result = array('code' => 0, 'msg' => '', 'data' => array());
        try {
            if (!isset($params['department_id'])) {
                $params['department_id'] = self::CONF_DEFAULT_DEPARTMENT_ID;
            }
            if (!isset($params['fetch_child'])) {
                $params['fetch_child'] = 1;
            }
            if (!isset($params['status'])) {
                $params['status'] = 0;
            }
            $resp = self::curlApi(self::API_USER_LIST, self::CURL_METHOD_GET, $params);
            $result['code'] = $resp['errcode'];
            $result['msg'] = empty($resp['errmsg']) ? '' : $resp['errmsg'];
            $result['data'] = (empty($resp['userlist']) || !is_array($resp['userlist'])) ? array() : $resp['userlist'];;
        } catch (Exception $e) {
            $result['code'] = self::ERROR_CODE_FAIL;
            $result['msg'] = $e->getMessage();
        }
        return $result;
    }

    private static function curlApi($apiType, $method, $params = array())
    {
        try {
            switch ($method) {
                case self::CURL_METHOD_POST:
                    $url = self::getUrl($apiType);
                    break;
                case self::CURL_METHOD_GET:
                default:
                    $url = self::getUrl($apiType, $params);
                    break;
            }
            $try = self::getConf('try', 2);
            while ($try) {
                $try--;
                switch ($method) {
                    case self::CURL_METHOD_POST:
                        $cres = Curl::post($url, json_encode($params, JSON_UNESCAPED_UNICODE));
                        break;
                    case self::CURL_METHOD_GET:
                    default:
                        $cres = Curl::get($url);
                        break;
                }
                if ($cres['err']) {
                    if ($try) {
                        continue;
                    }
                    throw new Exception('CurlErr:' . $cres['err'], self::ERROR_CODE_FAIL);
                }
                if (empty($cres['content'])) {
                    if ($try) {
                        continue;
                    }
                    throw new Exception('response content empty', self::ERROR_CODE_FAIL);
                }
                if (!($resp = json_decode($cres['content'], true))) {
                    if ($try) {
                        continue;
                    }
                    throw new Exception('response content invalid:' . $cres['content'], self::ERROR_CODE_FAIL);
                }
                if (!isset($resp['errcode'])) {
                    if ($try) {
                        continue;
                    }
                    throw new Exception('response errcode invalid', self::ERROR_CODE_FAIL);
                }
                return $resp;
            }
        } catch (Exception $e) {
            throw new Exception($e->getMessage(), self::ERROR_CODE_FAIL);
        }
    }

    private static function getUrl($apiType, $query = array())
    {
        if (!is_array($query)) {
            $query = array();
        }
        $query['access_token'] = self::getToken();
        $queryStr = http_build_query($query);
        switch ($apiType) {
            case self::API_DEPARTMENT_LIST:
                return 'https://qyapi.weixin.qq.com/cgi-bin/department/list?' . $queryStr;
                break;
            case self::API_USER_CREATE:
                return 'https://qyapi.weixin.qq.com/cgi-bin/user/create?' . $queryStr;
                break;
            case self::API_USER_UPDATE:
                return 'https://qyapi.weixin.qq.com/cgi-bin/user/update?' . $queryStr;
                break;
            case self::API_USER_LIST:
                return 'https://qyapi.weixin.qq.com/cgi-bin/user/list?' . $queryStr;
                break;
            case self::API_USER_GET:
                return 'https://qyapi.weixin.qq.com/cgi-bin/user/get?' . $queryStr;
                break;
            case self::API_MESSAGE_SEND:
                return 'https://qyapi.weixin.qq.com/cgi-bin/message/send?' . $queryStr;
                break;
            default:
                return '';
                break;
        }
    }

    private static function getToken()
    {
        $corpId = static::getConf('corp_id', self::CONF_DEFAULT_CORP_ID);
        $corpSecret = static::getConf('corp_secret', self::CONF_DEFAULT_CORP_SECRET);
        $tokenCacheKey = implode('_', array(MsggItem::TYPE_WECHAT, md5(implode('.', array($corpId, $corpSecret)))));
        if (empty($token = Yii::app()->cache->get($tokenCacheKey))) {
            $url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?' . http_build_query(array('corpid' => $corpId, 'corpsecret' => $corpSecret));
            $cres = Curl::get($url);
            if ($cres['err']) {
                throw new Exception($cres['err'], self::ERROR_CODE_FAIL);
            }
            if (empty($cres['content'])) {
                throw new Exception('get token fail:response content empty.', self::ERROR_CODE_FAIL);
            }
            if (!($resp = json_decode($cres['content'], true))) {
                throw new Exception('get token fail:response content decode fail', self::ERROR_CODE_FAIL);
            }
            if (empty($resp['access_token'])) {
                throw new Exception(':get token fail:token invalid', self::ERROR_CODE_FAIL);
            }
            if (empty($resp['expires_in'])) {
                throw new Exception(':get token fail:expires invalid', self::ERROR_CODE_FAIL);
            }
            $token = $resp['access_token'];
            Yii::app()->cache->set($tokenCacheKey, $resp['access_token'], (intval($resp['expires_in']) - 10));
        }
        return $token;
    }

    public static function getConf($name, $default = null)
    {
        return isset(static::$_conf[$name]) ? static::$_conf[$name] : $default;
    }

    public static function getOptions($attribute = '', $options = array())
    {
        if (!is_array($options)) {
            $options = array();
        }
        switch ($attribute) {
            case 'status':
                $options['0'] = '====';
                $options['1'] = '已关注';
                $options['2'] = '已禁用';
                $options['4'] = '未关注';
                break;
            case 'msg_type':
                $options[self::MSG_TYPE_TEXT] = '文本';
                $options[self::MSG_TYPE_IMAGE] = '图片';
                $options[self::MSG_TYPE_VOICE] = '语音';
                $options[self::MSG_TYPE_VIDEO] = '视频';
                $options[self::MSG_TYPE_FILE] = '文件';
                $options[self::MSG_TYPE_NEWS] = '文章';
                break;
            case 'department':
                $res = self::getDepartments();
                if (isset($res['code']) && ($res['code'] == 0) && !empty($res['data'])) {
                    foreach ($res['data'] as $department) {
                        if ($department['id'] == self::CONF_DEFAULT_DEPARTMENT_ID) {
                            $options[$department['id']] = $department['name'] . '(默认)';
                        } else {
                            $options[$department['id']] = $department['name'];
                        }
                    }
                } else {
                    $options[self::CONF_DEFAULT_DEPARTMENT_ID] = '默认';
                }
                break;
        }
        return $options;
    }
}
